package com.untrance.shop.core.dao.bean;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.repackaged.com.google.common.base.Preconditions;
import com.untrance.shop.common.filters.model.Filter;
import com.untrance.shop.common.utils.JpaUtils;
import com.untrance.shop.core.dao.MainDao;
import com.untrance.shop.core.factory.QueryBuilderFactory;
import com.untrance.shop.model.MainEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.util.Collection;
import java.util.List;

/**
 * @author eugene zadyra
 */
@Transactional(readOnly = true)
public class MainDaoBean<U extends MainEntity> implements MainDao<U> {

	private static final Logger log = LoggerFactory.getLogger(MainDaoBean.class);

	@Autowired
	QueryBuilderFactory queryBuilderFactory;

	private Class<U> type;
	protected EntityManager em;

	@PersistenceContext
	public void setEntityManager(EntityManager em) {
		this.em = em;
	}

	public EntityManager getEntityManager() {
		return em;
	}

	public MainDaoBean(Class<U> type) {
		this.type = type;
	}

	@Override
	public final U find(Key id) {
		Preconditions.checkNotNull(id, "Attempt to find " + type.getSimpleName() + " with id=NULL");
		U object = em.find(type, id);
		em.close();
		return object;
	}

	@Transactional(readOnly = false)
	public final <T extends MainEntity> T add(T object) {
		Preconditions.checkNotNull(object, "Can't add " + type.getSimpleName() + ", beacuse it equals NULL");
		em.persist(object);
		return object;
	}

	@Override
	@Transactional(readOnly = false)
	public final <T extends MainEntity> T update(T object) {
		Preconditions.checkNotNull(object, "Can't unpdate " + type.getSimpleName() + ", because it equals NULL");
		em.merge(object);
		return object;
	}

	@Override
	public final Collection<U> findAll() {
		Query query = em.createQuery("SELECT o FROM " + type.getName() + " o");
		return JpaUtils.getCheckedResultList(query);
	}

	@Override
	@Transactional(readOnly = false)
	public final void remove(Key id) {
		Preconditions.checkNotNull(id, "Can't remove " + type.getSimpleName() + " with id=NULL");
		U o = em.getReference(type, id);
		em.remove(o);
	}

	@Transactional(readOnly = false)
	public final int removeAll() {
		Query query = em.createQuery("DELETE o FROM " + type.getName() + " o");
		return query.executeUpdate();
	}

	@Override
	public Collection<U> findEntitiesByKeyCollection(Collection<Key> keys) {
		Query query = em.createQuery("SELECT o FROM " + type.getName() + " o" +
				" WHERE :keys.contains(o.id)"
		);
		query.setParameter("keys", keys);
		return JpaUtils.getCheckedResultList(query);
	}

	@Override
	public <F extends Filter> List<U> findEntitiesByFilter(F filter) {
		Preconditions.checkNotNull(filter, "Filter can not be NULL");
		return JpaUtils.getCheckedResultList(queryBuilderFactory.getQueryBuilder(filter).buildQuery());
	}

	@Override
	public <F extends Filter> Integer findEntitiesCountByFilter(F filter) {
		Preconditions.checkNotNull(filter, "Filter can not be NULL");
		return JpaUtils.getCount(queryBuilderFactory.getQueryBuilder(filter).buildCountQuery());
	}

	@Override
	public Integer countAll() {
		Query query = em.createQuery("SELECT COUNT(o) FROM " + type.getName() + " o");
		return JpaUtils.getCount(query);
	}
}
